//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<key_map.cpp>
///	@path	~/src/appear/app/
///	@date	2007/12/11
///	@desc	Key input mapping.

#include "config/config.h"

#include "appear/app/key_map.h"

namespace xeres {

	// KeyMap
	KeyMap::KeyMap( void )
	{
	}

	// ~KeyMap
	KeyMap::~KeyMap( void )
	{

	}

	// FindKey
	KeyMap::_KeyNote * KeyMap::FindKey( uint32 status , KeyInput::Key action_key )
	{
		std::pair< _KeyMap::iterator,
			_KeyMap::iterator > range = m_keynotes.equal_range( action_key );

		if( range.first == m_keynotes.end() && range.second == m_keynotes.end() )
			return NULL;

		for( _KeyMap::iterator it = range.first ; it != range.second ; ++it )
		{
			_KeyNote& note = it->second;

			// matches 3 status
			int shift = note.m_status & Input::INPUT_STATE_SHIFT;
			int alt = note.m_status & Input::INPUT_STATE_ALT;
			int ctrl = note.m_status & Input::INPUT_STATE_CTRL;

			// shift
			if( shift && ( ( shift & status ) == 0 ) )
				continue;
			// alt
			if( alt && ( ( alt & status ) == 0 ) )
				continue;
			// ctrl
			if( ctrl && ( ( ctrl & status ) == 0 ) )
				continue;

			return &note;
		}
		return NULL;
	}

	// OnKeyInput
	HandleInput KeyMap::OnKeyInput( const KeyInput& input )
	{
		if( input.IsReleased() )
		{
			return INPUT_UNHANDLED;
		}

		// filter out combiner key
		switch( input.GetKey() )
		{
		case KeyInput::KEY_LSHIFT:
		case KeyInput::KEY_RSHIFT:
		case KeyInput::KEY_LCTRL:
		case KeyInput::KEY_RCTRL:
		case KeyInput::KEY_LALT:
		case KeyInput::KEY_RALT:
		case KeyInput::KEY_CAPSLOCK:
		case KeyInput::KEY_NUMLOCK:
		case KeyInput::KEY_SCRLOCK:
			return INPUT_UNHANDLED;
		}

		_KeyNote * note = FindKey(
			input.GetState() & Input::INPUT_STATE_KEY_COMBINER ,
			input.GetKey() );
		if( note && note->m_call != NULL )
		{
			(*note).m_call();
			return INPUT_HANDLED;
		}

		return INPUT_UNHANDLED;
	}

	// OnMouseInput
	HandleInput KeyMap::OnMouseInput( const MouseInput& input )
	{
		// nothing to handle
		return INPUT_UNHANDLED;
	}

	// SetKey
	bool KeyMap::SetKey( uint32 status , KeyInput::Key action_key ,
		function<void(void)> call , bool override /*= false */ )
	{
		// only accept key status
		if( ( status & ~Input::INPUT_STATE_KEY_COMBINER ) != 0 )
		{
			TRACE_WARNING( _S("KeyMap::SetKey: Only key combiners(shift,alt,ctrl) can be combined with action.") );
			status &= Input::INPUT_STATE_KEY_COMBINER;
			status &= ~Input::INPUT_STATE_KEY_REPEAT;
		}

		// check action key
		switch( action_key )
		{
		case KeyInput::KEY_LSHIFT:
		case KeyInput::KEY_RSHIFT:
		case KeyInput::KEY_LCTRL:
		case KeyInput::KEY_RCTRL:
		case KeyInput::KEY_LALT:
		case KeyInput::KEY_RALT:
		case KeyInput::KEY_CAPSLOCK:
		case KeyInput::KEY_NUMLOCK:
		case KeyInput::KEY_SCRLOCK:
			TRACE_ERROR( _S("KeyMap::SetKey: Keys that changes status cannot be action key.") );
			return false;
		}

		_KeyNote * note = FindKey( status , action_key );
		if( note != NULL )
		{
			if( !override )
			{
				TRACE_ERROR( _S("KeyMap::SetKey: Hot key for '%s' has been set.") ,
					KeyInput::GetInputString( action_key ).c_str() );
				return false;
			}
			else
			{
				note->m_status = status;
				note->m_key = action_key;
				note->m_call = call;
			}
		}
		else
		{
			if( call != NULL )
			{
				_KeyNote inserter;
				inserter.m_status = status;
				inserter.m_key = action_key;
				inserter.m_call = call;

				m_keynotes.insert( std::pair<KeyInput::Key,_KeyNote>(action_key,inserter) );
			}
		}
		return true;
	}

	// ClearAll
	void KeyMap::ClearAll( void )
	{
		m_keynotes.clear();
	}

} // namespace xeres
